/*

RAT dcom v0.5.0, Refractor2 Archive Tool decompressor

Copyright (C) 2002
					Tonto Rostenfaunt		tonto@linuxmail.org
					Blackjap McGee			blackjap@hawaii.rr.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extract.h"

#include "macread.h"
#include <CoreFoundation/CoreFoundation.h>

void rfacpy(unsigned char *, unsigned char *, unsigned int);
/*
BOOL WINAPI LibMain(HINSTANCE hDLL,DWORD Reason,LPVOID Reserved)
{	return 1; }
*/
int /*__declspec(dllexport)*/ extract(FILE *rfa, char *filename, unsigned long tucsize, unsigned long offset \
									,unsigned char *sbuff, unsigned char *obuff) {
	DHEAD	*datah;
	FILE	*o;			//output file
	unsigned long	curpos;		//current rfa filepos
	unsigned long	segments;
	unsigned int	headi=1;		//header index

	curpos = ftell(rfa);
	fseek(rfa,offset,SEEK_SET);
	readu32(&segments, rfa);
	datah = calloc(segments+1,12);
	memset(datah,0,(segments+1)*12);

	while(headi!=segments+1) {
		readu32(&datah[headi].csize,rfa);
		readu32(&datah[headi].ucsize,rfa);
		readu32(&datah[headi].doffset,rfa);
		headi++;
	}
	headi=1;	//reset

	o = fopen(filename,"wb");
    printf("Working on %s (compressed)\n", filename);

	while(headi!=segments+1) {

		memset(sbuff,0,datah[headi].csize);
		memset(obuff,0,datah[headi].ucsize);

		fseek(rfa,offset+((segments+segments*2)*4)+datah[headi].doffset+4,SEEK_SET);	//seek to segment offset
		fread(sbuff,datah[headi].csize,1,rfa);
		
		switch(_dcom(sbuff,obuff,datah[headi].csize,datah[headi].ucsize)) {
			case -12:
				fwrite(obuff,datah[headi].ucsize,1,o);
				headi++;
				continue;
			case -6:
				printf("-6: ptr < obuff.\n");
				fclose(o);
				return -6;
			case -5:
				printf("-5: ptr > obuff.\n");
				fclose(o);
				return -5;
			case -4:
				printf("-4: ptr > sbuff.\n");
				fclose(o);
				return -4;
			case -3:
				printf("-8: sbuff ptr is valid. control byte is not.\n");
				fclose(o);
				return -3;
			case '?':
				return 0;
		}
	}

	fclose(o);
	free(datah);
	fseek(rfa,curpos,SEEK_SET);	//restore offset
	return 0;

}


int _dcom(unsigned char *sbuff, unsigned char *obuff, unsigned int sbsize, unsigned int obsize)
{
 unsigned int  cb1=0,cbc1=0,distance=0,length=0;
 unsigned char *sb1,*ob1,*ob2;
 char *cb1_8=(char*)&cb1;
 unsigned int sbend=sbsize-3;
 sb1=sbuff; ob1=obuff; ob2=obuff;
    int cb2 = 0;
    
    *cb1_8 = *sb1++;
    cb1 = ilth(cb1);
    
    printf("cb1 is %d\n", cb1);
    
 if(cb1>17) {
   DBUG(1);
   cb1-=14;
   if(ob1+(cb1-3)>obuff+obsize) { OOB; return(-5); }
   if(sb1+(cb1-3)>sbuff+sbend) { OOB; return(-4); }
   cbc1=cb1>>2;
   cb1^=3;
   cb1&=3;
   DBUG(2);
   for(;cbc1!=0;cbc1--,sb1+=4,ob1+=4) {
     ob1[0]=sb1[0]; ob1[1]=sb1[1];
     ob1[2]=sb1[2]; ob1[3]=sb1[3];
   }
   sb1-=cb1;
     ob1-=cb1;
     *cb1_8 = *sb1++;
     cb1 = ilth(cb1);
 } else { goto firstcontrol; }


 while(1) {
START:
	 if(cb1>=0x40) {
		 distance=((cb1>>2)&7)+(*sb1++)*8;
		 length=(cb1>>5)+1;
		 ob2=ob1-(distance+1);
		 if(ob2<obuff) { OOB; return(-6); }
		 if(distance<3) {
             if(ob1+length>obuff+obsize) { OOB; return(-5); }
			 for(;length!=0;length--,ob1++,ob2++)
				 *ob1=*ob2;
		 } else {
		 rfacpy(ob1,ob2,length);
		 ob1+=length;
			 }
		 length=distance=0;
	 }
	 else if(cb1>=0x20) {
		 length=cb1&31;
		 if(length==0) {
			 while((*sb1++)==0) {
				 length+=255;
				 if(sb1+3>sbuff+sbend) { OOB; return(-4); }
			 }
			 length+=(*(sb1-1))+33;
		 } else length+=2;
		 distance=(*(unsigned short int *)&sb1[0])>>2;
         distance = slth(distance);
         sb1+=2;
		 ob2=ob1-(distance+1);
		 if(ob2<obuff) { OOB; return(-6); }
         if(distance<3) {
			 if(ob1+length>obuff+obsize) { OOB; return(-5); }
			 for(;length!=0;length--,ob1++,ob2++)
				 *ob1=*ob2;
		 } else {
		 rfacpy(ob1,ob2,length);
		 ob1+=length;
			 }
		 length=distance=0;
	 } else if(cb1>=0x10) {
		 length=cb1&7;
		 if(length==0) {
			 while((*sb1++)==0) {
				 length+=255;
				 if(sb1+3>sbuff+sbend) { OOB; return(-14); }
			 }
             int additionalLength = (*(sb1-1)) + 9;
			 length+=additionalLength;
		 }else length+=2;
		 distance=(*(unsigned short int *)&sb1[0]);
         distance = slth(distance);
         sb1+=2;
		 ob2=ob1-16384;
		 if((distance>>=2)==0) {
			 if(sb1==(sbuff+sbend+3)) { return -12; }
			 if(ob1==(obuff+obsize)) { return -12; }
			 if(ob1>obuff+obsize) { OOB; return(-5); }
			 return((sb1>sbuff+sbend?-4:-8));
		 }
		 ob2-=distance;
		 rfacpy(ob1,ob2,length);
		 ob1+=length;
		 length=distance=0;
	 } else if(cb1<0x10) {
		 if(ob1>obuff+obsize) { OOB; return(-5); }
		 distance=(cb1>>2)+(*sb1++)*4;
		 ob2=ob1-(distance+1);
		 if(ob2<obuff) { OOB; return(-6); }
		 ob1[0]=ob2[0];
		 ob1[1]=ob2[1];
		 //		 memcpy(ob1,ob2,2);
		 ob1+=2;
	 }

	 cb1=*(sb1-2)&3;

	 while(cb1==0) {
		 if(sb1>sbuff+sbend) { OOB; return(-4); }
		 cb1=*sb1++;
	firstcontrol:
		 if(cb1>=0x10) goto START;
		 if(cb1==0) {
			 while((*sb1++)==0) {
				 cb1+=255;
				 if(sb1+cb1+18>sbuff+sbend) { OOB; return(-4); }
			 }
			 cb1+=*(sb1-1)+21;
		 } else cb1+=6;
		 if(ob1+cb1-3>obuff+obsize) { OOB; return(-5); }
		 if(sb1+cb1-3>sbuff+sbend) { OOB; return(-4); }
		 length=(cb1>>2);
		 cb1^=3;
		 cb1&=3;
		 length=(length*4)-cb1;
		 memcpy(ob1,sb1,length);
		 sb1+=length;
		 ob1+=length;
		 length=0;
		 cb1=*sb1++;
		 if(cb1>=0x10) goto START;
		 if(ob1+3>obuff+obsize) { OOB; return(-5); }
		 distance=(cb1>>2)+(*sb1++)*4;
		 ob2=ob1-(distance+1)-2048;
		 if(ob2<obuff) { OOB; return(-6); }
		 memcpy(ob1,ob2,4);
		 ob1+=3;
		 cb1=*(sb1-2)&3;
	 }
 	 if(ob1+cb1>obuff+obsize) { OOB; return(-5); }
	 if(sb1+cb1>sbuff+sbend) { OOB; return(-4); }
	 memcpy(ob1,sb1,4);
	 sb1+=cb1;
	 ob1+=cb1;
	 cb1=*sb1++;
 }
 return 0;
}

void rfacpy(unsigned char *dest, unsigned char *source, unsigned int length)
{
	length+=3;
	length>>=2;
	for(;length!=0;length--,source+=4,dest+=4) {
		dest[0]=source[0]; dest[1]=source[1];
		dest[2]=source[2]; dest[3]=source[3];
	}
}

